package com.xunhang.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xunhang.dto.AdminDTO;
import com.xunhang.dto.Result;
import com.xunhang.entity.Admin;
import com.xunhang.entity.News;
import com.xunhang.mapper.AdminMapper;
import com.xunhang.mapper.NewsMapper;
import com.xunhang.service.IAdminService;
import com.xunhang.service.INewsService;
import com.xunhang.utils.AdminHolder;
import com.xunhang.utils.CacheClient;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.xunhang.utils.Constants.*;

@Service
public class NewsService extends ServiceImpl<NewsMapper, News> implements INewsService {
    @Autowired
    RedisTemplate redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;
    private final CacheClient cacheClient;
    private final RedissonClient redissonClient;
    private final RBloomFilter<Integer> bloomFilter;

    public NewsService(StringRedisTemplate stringRedisTemplate,CacheClient cacheClient,RedissonClient redissonClient){
        this.stringRedisTemplate=stringRedisTemplate;
        this.cacheClient=cacheClient;
        this.redissonClient=redissonClient;
        bloomFilter=redissonClient.getBloomFilter("news");
        bloomFilter.tryInit(500000,0.01);
    }
    @PostConstruct
    private void initialBlooMFilter(){
        cacheClient.initialBloomFilter(getBaseMapper(),bloomFilter,News.class);
    }

    //ok
    public Result queryNewsById(Integer id){
        News news = cacheClient.queryWithPassThrough(NEWS_KEY, id, bloomFilter, super::getById, News.class);
        AdminDTO admin = AdminHolder.getAdmin();
        if(news.getIsPublish()!=true&&admin==null){
            return  Result.fail("需要管理员权限");
        }
        String pageViewsKey=PAGE_VIEWS_KEY+news.getId();
        if(stringRedisTemplate.hasKey(pageViewsKey)){
           stringRedisTemplate.opsForValue().increment(pageViewsKey);
        }else {
            stringRedisTemplate.opsForValue().set(pageViewsKey,news.getPageviews().toString());
        }
        long pageviews = Long.parseLong(stringRedisTemplate.opsForValue().get(pageViewsKey));
        news.setPageviews(pageviews);
        return Result.ok(news);
    }

    //OK
    public Result queryHotNews(){
        AdminDTO admin = AdminHolder.getAdmin();
        if(admin==null){
            List<News> hotNews = super.query()
                    .eq("ishot", true)
                    .eq("is_publish",true)
                    .list();
        }
        List<News> hotNews = super.query().eq("ishot", true).list();
        return Result.ok(hotNews,(long) hotNews.size());
    }

    public Result saveNews(News news){
        AdminDTO admin = AdminHolder.getAdmin();
        if(admin==null){
            return  Result.fail("需要管理员权限");
        }
        boolean save = super.save(news);
        if(save){
            bloomFilter.add(news.getId());
            return Result.ok();
        }else {
            return Result.fail("插入失败");
        }
    }
    //ok
    public Result updateNews(News news){
        AdminDTO admin = AdminHolder.getAdmin();
        if(admin==null){
            return  Result.fail("需要管理员权限");
        }
        if(news==null||news.getId()==null){
            return Result.fail("请给出id");
        }
        String pageViewsKey=PAGE_VIEWS_KEY+news.getId();
        if(news.getPageviews()==null&&stringRedisTemplate.hasKey(pageViewsKey)){
            news.setPageviews(Long.parseLong(stringRedisTemplate.opsForValue().get(pageViewsKey)));
        }
        Boolean update= cacheClient.updateInDataBase(NEWS_KEY, news.getId(), news, bloomFilter, super::updateById);
        if(update){
            stringRedisTemplate.delete(pageViewsKey);
            return Result.ok();
        }else {
            return Result.fail("更新失败");
        }
    }
    //ok
    public Result deleteNews(Integer id){
        AdminDTO admin = AdminHolder.getAdmin();
        if(admin==null){
            return  Result.fail("需要管理员权限");
        }
        if (id==null){
           return Result.fail("删除失败");
        }
        Boolean remove = cacheClient.deleteInDataBase(NEWS_KEY, id, bloomFilter, super::removeById);
        if(remove){
            return Result.ok();
        }else {
            return Result.fail("删除失败");
        }
    }


    public Result queryNewsByType(Integer tid, Integer current, LocalDateTime previousTime,LocalDateTime followingTime,int queryType){
        if(tid==null||current==null){
            return  Result.fail("参数错误");
        }
        AdminDTO admin = AdminHolder.getAdmin();
        if(admin==null){
            if (previousTime==null||followingTime==null){
                if(queryType==1){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .eq("type_id", tid)
                            .eq("is_publish",true)
                            .orderByDesc("submittime");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }else if(queryType==2){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .eq("type_id", tid)
                            .eq("is_publish",true)
                            .orderByDesc("pageviews");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }
            }else {
                if(queryType==1){
                    LambdaQueryChainWrapper<News> lambdaWrapper = new LambdaQueryChainWrapper<>(News.class)
                            .gt(News::getSubmitTime, previousTime) // 大于开始时间
                            .lt(News::getSubmitTime, followingTime)
                            .eq(News::getIsPublish,true)
                            .orderByAsc(News::getSubmitTime);
                    Page<News> page = lambdaWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    if(newsList==null){
                        return Result.fail("查询失败");
                    }else {
                        return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                    }
                }else {
                    return Result.ok();
                }
            }
        }
        if (previousTime==null||followingTime==null){
            if(queryType==1){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query().eq("type_id", tid).orderByDesc("submittime");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }else if(queryType==2){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query().eq("type_id", tid).orderByDesc("pageviews");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }
        }else {
            if(queryType==1){
                LambdaQueryChainWrapper<News> lambdaWrapper = new LambdaQueryChainWrapper<>(News.class)
                        .gt(News::getSubmitTime, previousTime) // 大于开始时间
                        .lt(News::getSubmitTime, followingTime)
                        .orderByAsc(News::getSubmitTime);
                Page<News> page = lambdaWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                if(newsList==null){
                    return Result.fail("查询失败");
                }else {
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }
            }else {
                return Result.ok();
            }
        }
        return null;
    }

    public Result searchNews(String key,Integer current,int queryType){

        AdminDTO admin=AdminHolder.getAdmin();
        if(admin==null){
            if(current==null){
                return  Result.fail("参数错误");
            }else if(key==null||key.isEmpty()){
                if(queryType==1){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .eq("is_publish",true)
                            .orderByDesc("submittime");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }else if(queryType==2){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .eq("is_publish",true)
                            .orderByDesc("pageviews");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }
            }else{
                if(queryType==1){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .like("title", key)
                            .or()
                            .like("detail", key)
                            .eq("is_publish",true)
                            .orderByDesc("submittime");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }else if (queryType==2){
                    QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                            .like("title", key)
                            .or()
                            .like("detail", key)
                            .eq("is_publish",true)
                            .orderByDesc("pageviews");
                    Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                    List<News> newsList = page.getRecords();
                    Long count = page.getTotal();
                    return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
                }
            }
        }
        if(current==null){
            return  Result.fail("参数错误");
        }else if(key==null||key.isEmpty()){
            if(queryType==1){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                        .orderByDesc("submittime");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }else if(queryType==2){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                        .orderByDesc("pageviews");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }
        }else{
            if(queryType==1){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                        .like("title", key)
                        .or()
                        .like("detail", key)
                        .orderByDesc("submittime");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }else if (queryType==2){
                QueryChainWrapper<News> newsQueryChainWrapper = super.query()
                        .like("title", key)
                        .or()
                        .like("detail", key)
                        .orderByDesc("pageviews");
                Page<News> page = newsQueryChainWrapper.page(new Page<>(current, MAX_PAGE_SIZE));
                List<News> newsList = page.getRecords();
                Long count = page.getTotal();
                return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
            }
        }
        return null;
    }

    public Result queryAllNews(Integer current,int queryType){
        AdminDTO admin = AdminHolder.getAdmin();
        List<News> newsList=new ArrayList<>();
        Long count=0L;
        if (admin==null){
            if (queryType==1){
                Page<News> page = super.query()
                        .eq("is_publish", true)
                        .orderByAsc("submittime")
                        .page(new Page<>(current, MAX_PAGE_SIZE));
                newsList=page.getRecords();
                count=page.getTotal();
            }else if(queryType==2){
                Page<News> page= super.query()
                        .eq("is_publish",true)
                        .orderByDesc("pageviews")
                        .page(new Page<>(current, MAX_PAGE_SIZE));
                newsList=page.getRecords();
                count=page.getTotal();
            }
        }else {
            if (queryType==1){
                Page<News> page= super.query()
                        .orderByAsc("submittime")
                        .page(new Page<>(current, MAX_PAGE_SIZE));
                newsList=page.getRecords();
                count=page.getTotal();
            }else if(queryType==2){
                Page<News> page= super.query()
                        .orderByDesc("pageviews")
                        .page(new Page<>(current, MAX_PAGE_SIZE));
                newsList=page.getRecords();
                count=page.getTotal();
            }
        }
        return Result.ok(newsList,count/MAX_PAGE_SIZE+1);
    }

    public Result addPageViews(Integer nid,int add) throws InterruptedException {
        RLock lock = redissonClient.getLock(NEWS_KEY + nid);
        boolean b = lock.tryLock(3,30, TimeUnit.SECONDS);
        if(b){
            News news = getBaseMapper().selectById(nid);
            news.setPageviews(news.getPageviews()+add);
            getBaseMapper().updateById(news);
            lock.unlock();
            return Result.ok();
        }else {
            return Result.fail("服务器繁忙");
        }
    }

    @Scheduled(fixedDelay = 5 * 60 * 1000) // 每5分钟同步
    public void syncViewCount(){
        Set<String> keys = stringRedisTemplate.keys(PAGE_VIEWS_KEY+"*");
        keys.forEach(key -> {
            Integer articleId = Integer.parseInt(key.split(":")[2]);
            Long pageviews=Long.parseLong(stringRedisTemplate.opsForValue().get(key));
            News news=new News();
            news.setId(articleId);
            news.setPageviews(pageviews);
            updateNews(news);
        });
        stringRedisTemplate.delete(keys);
    }
}
